from tkinter.messagebox import NO
import time
import caminput as ci
import tcpinput as ti
import cv2 as cv
import wx
import wx.lib.scrolledpanel as scrolled
from logger_panel import LoggerPanel
import numpy as np
import core_algorithm as core
from multiprocessing import Process, Queue,freeze_support
import threading
import sync_thread
import argparse

input_queue = Queue(1)
output_queue = Queue(1)
last_frame = None
yolov_perf = []
controller = None # Car
sync = None
last_status = ""
logger = None

controll_mode = 2

AUTO_MODE = 1
MANUAL_MODE = 2


class FramePanel(wx.Frame):
    input_menu_id = 100
    input_mode = 0
    camera_mode = 1001
    tcp_mode = 1002
    camera_menu_item_id = 101
    tcp_menu_item_id = 102
    input_thread = None
    bitmap = None
    font = None

    def __init__(self, *args, **kw):
        self.filters = []
        self.filter_id = 0
        self.carAddress = '0.0.0.0'
        self.set = 0
        self.is_disconnect = False
        self.config = kw['config']
        del kw['config']
        fps = kw.get('fps', 24)
        del kw['fps']
        if 'size' not in kw:
            kw['size'] = (824, 700)
        super(FramePanel, self).__init__(*args, **kw)

        self.font = wx.Font(30, wx.ROMAN, wx.ITALIC, wx.NORMAL)
        self._make_menu_bar()
        self.timer = wx.Timer(self, 1)
        self.timer.Start(int(1000. / fps))

        self.top_pnl = wx.Panel(self, -1)

        wx.StaticText(self.top_pnl, pos=(5, 5), label='处理:')
        self.filters_choice = wx.Choice(parent=self.top_pnl, pos=(60, 2), size=(60, 40), choices=["无"])
        self.filters_choice.Bind(wx.EVT_CHOICE, self._on_filters_choice)
        def _c_auto(evenet):
            global controll_mode
            logger.log('Auto mode')
            controll_mode = AUTO_MODE
            controller.manual_idle = False
        def _c_forward(evenet):
            global controll_mode
            controll_mode = MANUAL_MODE
            logger.log('Forward')
            controller.manual_forward()
        def _c_backward(evenet):
            logger.log('Backward')
            global controll_mode
            controll_mode = MANUAL_MODE
            controller.manual_backward()
        def _c_stop(evenet):
            logger.log('stop')
            global controll_mode
            controll_mode = MANUAL_MODE
            controller.manual_stop()     
        wx.Button(self.top_pnl, pos=(10, 45),size = (150, 30), label="Auto")\
            .Bind(wx.EVT_LEFT_DOWN, handler= _c_auto)
        wx.Button(self.top_pnl, pos=(10, 78),size = (150, 30), label="Forward")\
            .Bind(wx.EVT_LEFT_DOWN, handler= _c_forward)
        wx.Button(self.top_pnl, pos=(10, 111),size = (150, 30), label="Backward")\
            .Bind(wx.EVT_LEFT_DOWN, handler= _c_backward)
        wx.Button(self.top_pnl, pos=(10, 144),size = (150, 30), label="STOP")\
            .Bind(wx.EVT_LEFT_DOWN, handler= _c_stop)
        self.pnl = wx.Panel(self.top_pnl, -1, pos=(170, 40), size=(650, 500))
        self.pnl.SetDoubleBuffered(True)
        self.pnl.SetBackgroundColour(wx.WHITE)
        self.pnl.Bind(wx.EVT_PAINT, self._on_paint)

        self.Bind(wx.EVT_TIMER, self._on_timer, id=1)
        self.Bind(wx.EVT_CLOSE, self._on_close)
        wx.StaticText(self.top_pnl, pos=(170, 542), label='Console控制台').SetBackgroundColour("Black")
        global logger
        logger = LoggerPanel(self.top_pnl, pos=(170, 564), size=(650, 100))



    def _make_menu_bar(self):
        open_menu = wx.Menu()
        open_menu.Append(self.camera_menu_item_id, "摄像头[Debug]", '以摄像头为输入')
        open_menu.Append(self.tcp_menu_item_id, "网络连接", '以网络源为输入')
        open_menu.Bind(wx.EVT_MENU, self._menu_open_camera, id=self.camera_menu_item_id)
        open_menu.Bind(wx.EVT_MENU, self._menu_open_tcp, id=self.tcp_menu_item_id)
        menu_bar = wx.MenuBar()
        menu_bar.Append(open_menu, "打开")
        self.SetMenuBar(menu_bar)

    def add_filter(self, label, fi):
        self.filters_choice.Append(label)
        self.filters.append(fi)
        self.filter_id += 1
        self.filters_choice.SetSelection(self.filter_id)

    def do_filters(self, data):
        if self.filter_id <= 0 or self.filter_id > len(self.filters):
            return data
        return self.filters[self.filter_id - 1](data)

    def _init_input_thread(self):
        self.input_thread.start()
        self.input_thread.retrieve()

    def wait_input_thread_quit(self):
        self.input_thread.set_quit()
        self.input_thread.join()

    def _menu_open_camera(self, event):
        if self.input_mode == self.camera_mode:
            return
        if self.input_mode != 0:
            self.wait_input_thread_quit()
            self.bitmap = None
        self.input_mode = self.camera_mode
        self.input_thread = ci.CameraInput()
        self._init_input_thread()

    def handle_disconnect(self):
        '''处理连接中断'''
        pass

    def handler_connect(self, addr):
        global controller
        global sync
        global last_status  
        controller = core.Car(addr)
        logger.log(f"发现车辆上线，以成功连接{addr}")
        #controller.inter_status = sync.GetStatus()
        print(f'forward_count = {controller.inter_status.forward_count}')
        last_status = controller.inter_status.to_str();

    def _menu_open_tcp(self, event):
        if self.input_mode == self.tcp_mode:
            return
        if self.input_mode != 0:
            self.wait_input_thread_quit()
            self.bitmap = None
            self.pnl.Refresh(eraseBackground=True)
        self.input_mode = self.tcp_mode
        global logger
        logger.log("设置为TCP模式")
        self.input_thread = ti.TcpInput(port = int(self.config['p']) if  self.config['p'] is not None else 2333)
        self.input_thread._on_connect_cb = self.handler_connect
        self.input_thread.on_disconnect = self.handle_disconnect
        self._init_input_thread()

    def _on_filters_choice(self, event):
        self.filter_id = self.filters_choice.GetSelection()

    def _on_timer(self, event):
        if self.input_thread is None:
            self.Refresh(eraseBackground=False)
            return
        if not self.input_thread.is_alive:
            self.timer.Stop()
            return

        data = self.input_thread.retrieve()

        if data is None:
            return
        distan = None
        curr_time_stamp = None
        if type(data) == type(()) and len(data) > 1:
            # curr_time_stamp = time.time()
            # print(f'curr_stamp:{curr_time_stamp:.3f} car_time_stamp: {data[2]} |  diff: {curr_time_stamp - data[2]}')
            curr_time_stamp = data[2]
            distan = data[1]
            data = data[0]
        h, w = data.shape[:2]
        data = self.do_filters((data, distan, curr_time_stamp))
        # test data is not None
        if data is None:
            return
        data = cv.cvtColor(data, cv.COLOR_BGR2RGB)
        if self.bitmap is not None:
            self.bitmap.CopyFromBuffer(data)
        else:
            self.bitmap = wx.Bitmap.FromBuffer(w, h, data)
        self.Refresh(eraseBackground=False)

    def _on_close(self, event):
        global yolov_perf
        if self.input_thread is not None:
            self.input_thread.set_quit()
        
        self.Destroy()

    def _on_paint(self, event):
        device_context = wx.BufferedPaintDC(self.pnl)
        if self.bitmap is None:
            device_context.SetBackground(wx.BLACK_BRUSH)
            device_context.Clear()
            device_context.SetTextForeground(wx.WHITE)
            device_context.SetFont(self.font)
            device_context.DrawText("No signal", 260, 160)
            device_context.DrawText("无 信 号", 260, 200)
        else:
            device_context.DrawBitmap(self.bitmap, 0, 0)


# 在这里写函数处理数据
# 函数的输入必须一个[image]类型的参数，也必须返回[image]
# 以下三个为演示
# 注意要注册你编写的函数

def gray_filter(data):
    return data[0]



class YoloProcess(Process):
    def __init__(self, detect, in_queue: Queue, ou_queue: Queue):
        super().__init__(daemon=True)
        self.detect = detect
        self.in_queue: Queue = in_queue
        self.out_queue: Queue = ou_queue
        

    def run(self):
        global yolov_perf
        input_queue = Queue(1)
        output_queue = Queue(1)
        self.detect(source=':mem:', in_queue = input_queue, output_queue = output_queue)
        while True:
            input_image, distance, time_stamp = self.in_queue.get()
            input_queue.put(input_image)
            output_image = output_queue.get()
            # t_start = time.perf_counter()
            self.out_queue.put((output_image, distance, time_stamp))
            # print(f'yolov5 network time cost: {time.perf_counter() - t_start:.3f}s')
            

class DemoApp(wx.App):

    def __init__(self,redirect=False, filename=None, useBestVisual=False, clearSigInt=True, ):
        super().__init__(redirect, filename, useBestVisual, clearSigInt)

    def OnInit(self):
        # ======= 启动各个进程和线程 START ===========
        try:
            from yolov5 import detect
        except Exception as e:
            print(e)
            print('缺少yolov5')
            exit(1)
        worker = YoloProcess(detect.run, input_queue, output_queue)
        worker.start()
        # ======= 启动各个进程和线程 END ===========
        parser = argparse.ArgumentParser(prog="project-z")
        parser.add_argument('-p',action='store', help="指定服务运行的端口")
        config = vars(parser.parse_args())
        panel = FramePanel(None, title='EdgeSever控制面板', fps=24, config = config)
        sync = sync_thread.SyncThread('', port=int(config['p']) if config['p'] is not None else 2333, logger=logger)
        sync.start()
        panel.add_filter('预览', gray_filter)
        def yolov_func(data):
            '''  '''
            global last_frame
            global last_status
            global controll_mode
            if not input_queue.full():
                input_queue.put_nowait(data)
            if not output_queue.empty():
                (last_frame, detected_objs), distance, time_stamp = output_queue.get()
                # print(f'distance = {distance} det = {detected_objs}')
                # filter_obj = [o for o in detected_objs if o[0] in ['bottle','wine glass','vase']]
                # print(f'time_stamp_diff: {int(time.time()) - time_stamp}') 
                assert controller is not None
                # print(controll_mode)
                if controll_mode == AUTO_MODE:
                    controller.auto_mode(detected_objs, distance)
                    if controller.inter_status.to_str() != last_status:
                        print('change detected')
                        last_status = controller.inter_status.to_str()
                        sync.SendStatus(controller.inter_status)
                elif controll_mode == MANUAL_MODE:
                    pass 
            return last_frame
        panel.add_filter('yolov5', yolov_func)
        self.SetTopWindow(panel)
        panel.Show()
        
        return True

if __name__ == "__main__":
    app = DemoApp()
    app.MainLoop()
